package com.chen.time.timing.interpolation;

import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;

import com.chen.time.timing.Animator;
import com.chen.time.timing.TimingAdapter;

public class PropertySetter extends TimingAdapter {

    private Object object;
    private String propertyName;
    private KeyFrames keyFrames;
    private Method setMethod;
    private Method getMethod;

    public static Animator createAnimator(int duration, Object object, String propertyName,
            KeyFrames keyFrames) {
        PropertySetter ps = new PropertySetter(object, propertyName, keyFrames);
        Animator animator = new Animator(duration, ps);
        return animator;
    }

    public static <T> Animator createAnimator(int duration, Object object, String propertyName, T... params) {
        PropertySetter ps = new PropertySetter(object, propertyName, params);
        Animator animator = new Animator(duration, ps);
        return animator;
    }

    public static <T> Animator createAnimator(int duration, Object object, String propertyName,
            Evaluator evaluator, T... params) {
        PropertySetter ps = new PropertySetter(object, propertyName, evaluator, params);
        Animator animator = new Animator(duration, ps);
        return animator;
    }

    public PropertySetter(Object object, String propertyName, KeyFrames keyFrames) {
        this.object = object;
        this.propertyName = propertyName;
        this.keyFrames = keyFrames;
        setupMethodInfo();
    }

    public <T> PropertySetter(Object object, String propertyName, T... params) {
        this(object, propertyName, new KeyFrames(KeyValues.create(params)));
    }

    public <T> PropertySetter(Object object, String propertyName, Evaluator evaluator, T... params) {
        this(object, propertyName, new KeyFrames(KeyValues.create(evaluator, params)));
    }

    private void setupMethodInfo() {
        try {
            String firstChar = propertyName.substring(0, 1);
            String remainder = propertyName.substring(1);
            String setterName = "set" + firstChar.toUpperCase() + remainder;

            PropertyDescriptor prop = new PropertyDescriptor(propertyName, object.getClass(), null,
                    setterName);
            setMethod = prop.getWriteMethod();
            if (isToAnimation()) {
                String propertyGetterName = "get" + firstChar.toUpperCase() + remainder;
                prop = new PropertyDescriptor(propertyName, object.getClass(), propertyGetterName, null);
                getMethod = prop.getReadMethod();
            }
        } catch (Exception e) {
            throw new RuntimeException("Cannot find property methods: " + e);
        }
    }

    @Override
    public void begin() {
        if (isToAnimation()) {
            try {
                setStartValue(getMethod.invoke(object));
            } catch (Exception e) {
                System.out.println("Problem setting start value on object " + object + ": " + e);
            }
        }
    }

    @Override
    public void timingEvent(float fraction) {
        try {
            setValue(object, setMethod, fraction);
        } catch (Exception e) {
            System.out.println("Problem calling setValue in " + "PropertySetter.timingEvent: " + e);
        }
    }

    private void setStartValue(Object object) {
        keyFrames.getKeyValues().setStartValue(object);
    }

    private void setValue(Object object, Method method, float fraction) {
        try {
            method.invoke(object, keyFrames.getValue(fraction));
        } catch (Exception e) {
            System.out.println("Problem invoking method " + setMethod + " in object " + object
                    + " in setValue" + e);
        }
    }

    private boolean isToAnimation() {
        return keyFrames.getKeyValues().isToAnimation();
    }

}
